package kv

import (
	"database/sql"
	"gitee.com/tziye/renai-go/common"
	"gitee.com/tziye/renai-go/renai"
	"gitee.com/tziye/renai-go/sqlproxy"
)

type ListOperator struct {
	RenaiClient *renai.RenaiClient
	Db          *sql.DB
}

func NewListOperator(renaiClient *renai.RenaiClient) *ListOperator {
	return &ListOperator{
		RenaiClient: renaiClient,
		Db:          renaiClient.Db,
	}
}

func (listOperator *ListOperator) SaveKey(key string, expireSeconds int) {
	saveKey(listOperator.Db, key, expireSeconds, List)
}

func (listOperator *ListOperator) Key(key string) RenaiKey {
	return queryKey(listOperator.Db, key, List)
}

func (listOperator *ListOperator) Keys(keys []string) []RenaiKey {
	return queryKeys(listOperator.Db, keys, List)
}

func (listOperator *ListOperator) AllKeys() []RenaiKey {
	return keysByType(listOperator.Db, List)
}

func (listOperator *ListOperator) Like(pattern string) []RenaiKey {
	return keysLike(listOperator.Db, pattern, List)
}

func (listOperator *ListOperator) Regexp(pattern string) []RenaiKey {
	return keysRegexp(listOperator.Db, pattern, List)
}

func (listOperator *ListOperator) Values(key string) []RenaiValue {
	return values(listOperator.Db, key, List)
}

func (listOperator *ListOperator) Total() int {
	return total(listOperator.Db, List)
}

func (listOperator *ListOperator) Size(key string) int {
	return size(listOperator.Db, key, List)
}

func (listOperator *ListOperator) Expire(key string, expireSeconds int) {
	expire(listOperator.Db, key, expireSeconds, List)
}

func (listOperator *ListOperator) Exists(key string) bool {
	return exists(listOperator.Db, key, List)
}

func (listOperator *ListOperator) Contains(key string, value any) bool {
	return contains(listOperator.Db, key, value, List)
}

func (listOperator *ListOperator) Clear(key string) int {
	return clear(listOperator.Db, key, List)
}

func (listOperator *ListOperator) Remove(key string, value any) bool {
	return remove(listOperator.Db, key, value, List)
}

func (listOperator *ListOperator) BatchRemove(key string, values []any) int {
	return batchRemove(listOperator.Db, key, values, List)
}

func (listOperator *ListOperator) Delete(key string) {
	delete(listOperator.Db, key, List)
}

func (listOperator *ListOperator) BatchDelete(keys []string) {
	batchDelete(listOperator.Db, keys, List)
}

func (listOperator *ListOperator) Flush() {
	flush(listOperator.Db, List)
}

func (listOperator *ListOperator) Members(key string) []string {
	renaiValues := values(listOperator.Db, key, List)
	values := make([]string, 0)
	for _, rv := range renaiValues {
		values = append(values, rv.V)
	}
	return values
}

func (listOperator *ListOperator) Add(key string, value any) {
	listOperator.commonInsert(key, -1, []any{value}, 1)
}

func (listOperator *ListOperator) BatchAdd(key string, values []any) {
	listOperator.commonInsert(key, -1, values, 1)
}

func (listOperator *ListOperator) AddIfAbsent(key string, value any) {
	listOperator.commonInsert(key, -1, []any{value}, 2)
}

func (listOperator *ListOperator) BatchAddIfAbsent(key string, values []any) {
	listOperator.commonInsert(key, -1, values, 2)
}

func (listOperator *ListOperator) Insert(key string, idx int, value any) {
	listOperator.commonInsert(key, idx, []any{value}, 1)
}

func (listOperator *ListOperator) BatchInsert(key string, idx int, values []any) {
	listOperator.commonInsert(key, idx, values, 1)
}

func (listOperator *ListOperator) commonInsert(key string, idx int, values []any, flag int) {
	sqlproxy.CallRow(listOperator.Db, common.KV_LIST_INSERT, key, common.String(values), idx, flag)
}

func (listOperator *ListOperator) Get(key string, idx int) string {
	values := listOperator.commonGet(key, idx, 1, false)
	if len(values) == 0 {
		return ""
	}
	return values[0]
}

func (listOperator *ListOperator) BatchGet(key string, idx int, count int) []string {
	return listOperator.commonGet(key, idx, count, false)
}

func (listOperator *ListOperator) RevGet(key string, idx int) string {
	values := listOperator.commonGet(key, idx, 1, true)
	if len(values) == 0 {
		return ""
	}
	return values[0]
}

func (listOperator *ListOperator) RevBatchGet(key string, idx int, count int) []string {
	return listOperator.commonGet(key, idx, count, true)
}

func (listOperator *ListOperator) commonGet(key string, start int, count int, rev bool) []string {
	rows := listOperator.commonRange(key, start, count, rev, 1)
	return fetchOnlyValue(rows)
}

func fetchOnlyValue(rows *sql.Rows) []string {
	defer rows.Close()
	values := make([]string, 0)
	for rows.Next() {
		var value sql.NullString
		var score sql.NullInt64
		err := rows.Scan(&value, &score)
		common.PanicErr(err)
		values = append(values, value.String)
	}
	return values
}

func (listOperator *ListOperator) Pop(key string, idx int) string {
	values := listOperator.commonPop(key, idx, 1, false)
	if len(values) == 0 {
		return ""
	}
	return values[0]
}

func (listOperator *ListOperator) BatchPop(key string, idx int, count int) []string {
	return listOperator.commonPop(key, idx, count, false)
}

func (listOperator *ListOperator) RevPop(key string, idx int) string {
	values := listOperator.commonPop(key, idx, 1, true)
	if len(values) == 0 {
		return ""
	}
	return values[0]
}

func (listOperator *ListOperator) RevBatchPop(key string, idx int, count int) []string {
	return listOperator.commonPop(key, idx, count, true)
}

func (listOperator *ListOperator) commonPop(key string, start int, count int, rev bool) []string {
	rows := listOperator.commonRange(key, start, count, rev, 3)
	return fetchOnlyValue(rows)
}

func (listOperator *ListOperator) DeleteValue(key string, idx int, count int) int {
	return listOperator.commonDelete(key, idx, count, false)
}

func (listOperator *ListOperator) RevDeleteValue(key string, idx int, count int) int {
	return listOperator.commonDelete(key, idx, count, true)
}

func (listOperator *ListOperator) commonDelete(key string, start int, count int, rev bool) int {
	rows := listOperator.commonRange(key, start, count, rev, 2)
	return fetchUpdated(rows)
}

func (listOperator *ListOperator) commonRange(key string, start int, count int, rev bool, flag int) *sql.Rows {
	r := 1
	if !rev {
		r = -1
	}
	return sqlproxy.CallRows(listOperator.Db, common.KV_LIST_RANGE, key, start, count, r, flag)
}

func (listOperator *ListOperator) Replace(key string, oldValue any, newValue any) int {
	s := "UPDATE renai_kv_list_value_valid SET v = ? WHERE k =? AND v = ?"
	return sqlproxy.Exec(listOperator.Db, s, common.String(newValue), key, common.String(oldValue))
}

func (listOperator *ListOperator) IndexOf(key string, value any) []int {
	s := "SELECT score FROM renai_kv_list_value_valid WHERE k = ? AND v = ? ORDER BY score"
	rows := sqlproxy.QueryRows(listOperator.Db, s, key, common.String(value))
	return fetchRank(rows)
}

func (listOperator *ListOperator) FirstIndexOf(key string, value any) int {
	idx := listOperator.IndexOf(key, value)
	if len(idx) > 0 {
		return idx[0]
	}
	return -1
}

func (listOperator *ListOperator) LastIndexOf(key string, value any) int {
	idx := listOperator.IndexOf(key, value)
	if len(idx) > 0 {
		return idx[len(idx)-1]
	}
	return -1
}

func fetchRank(rows *sql.Rows) []int {
	defer rows.Close()
	values := make([]int, 0)
	for rows.Next() {
		var value sql.NullInt64
		err := rows.Scan(&value)
		common.PanicErr(err)
		values = append(values, int(value.Int64))
	}
	return values
}
